/**
 * Author: Todd Kerpelman
 * Date: 5/17/12
 * Time: 2:46 PM
 *
 * Copyright 2012 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package net.kerp.hangoutSpace {

import Box2D.Collision.Shapes.b2CircleShape;
import Box2D.Collision.Shapes.b2PolygonShape;
import Box2D.Common.Math.b2Vec2;
import Box2D.Dynamics.b2Body;
import Box2D.Dynamics.b2BodyDef;
import Box2D.Dynamics.b2FixtureDef;
import Box2D.Dynamics.b2World;

import com.greensock.TweenAlign;

import net.kerp.ToddUtils;
import com.greensock.TweenLite;

import flash.display.Sprite;
import flash.net.URLRequest;

public class FloatyPerson extends Sprite{
  private var _myBody:b2Body;
  private var _costume:SpaceshipArt;
  private var _world:b2World;
  private var _trailData:TrailData;
  private var _hangoutUser:HangoutParticipant;

  public function FloatyPerson(myWorld:b2World, user:HangoutParticipant) {
    _world = myWorld;
    _hangoutUser = user;
    createPhysObj();
    createCostume();
    generateRandomPush();
  }

  // Add the artwork that goes with the physics body.
  private function createCostume():void {
    _costume = new SpaceshipArt();
    _trailData = new TrailData();
    _costume.profilePic.load(new URLRequest(_hangoutUser.imageUrl));
    addChild(_costume);
    updateyourself();
  }


  public function getTrailData():TrailData {
    return _trailData;
  }

  public function updateyourself():void {

    // Adjust the spaceship to try and turn in the direction it's moving
    turnToFaceMyDirection();
    
    // Update costume to world
    _costume.x = _myBody.GetPosition().x * HangoutInSpace.RATIO;
    _costume.y = _myBody.GetPosition().y * HangoutInSpace.RATIO;
    _costume.rotation = _myBody.GetAngle() * 180 / Math.PI;

    // Update trail data
    var centerPoint :b2Vec2 = _myBody.GetWorldPoint(new b2Vec2(0,0));
    _trailData.newPosition.x = centerPoint.x * HangoutInSpace.RATIO;
    _trailData.newPosition.y = centerPoint.y * HangoutInSpace.RATIO;
    _trailData.newRotation = _myBody.GetAngle() * 180.0 / Math.PI;


    // Volume is between 0 and 5. We'll map that to our trail data size of
    // 0 and 100
    var idealTrailSize:int = _hangoutUser.volume * 20;
    var trailDiff:int = idealTrailSize - _trailData.size;
    if (Math.abs(trailDiff) < 5) {
      _trailData.size = idealTrailSize;
    } else {
      _trailData.size += (trailDiff / 5);
    }

  }

  /**
   * So I tried to make my trails look more natural by having the ships move
   * in the direction they were heading. The problem is this made these ships
   * end up "clustered" in the corners of the room.
   *
   * So, it turns out a much better approach was to make the ship turn to face
   * the direction it's already headed, and continue to move them around in a mostly
   * random way. You get nice looking trails that way, and the ship movement is
   * fun, in a wonky sort of way.
   *
   */
  private function turnToFaceMyDirection():void {

    var myFacing:Number = _myBody.GetAngle() - (Math.PI / 2);
    while (myFacing > Math.PI) { myFacing = myFacing - (2 * Math.PI);}
    while (myFacing < -Math.PI) {myFacing = myFacing + (2 * Math.PI);}

    // I think there's probably some simplifcation that I'm missing here
    var myVelocity:b2Vec2 = _myBody.GetLinearVelocity();
    var velocityFacing:Number = Math.atan2(myVelocity.y,myVelocity.x);

    var closestArcDist:Number = Math.abs(velocityFacing - myFacing);
    while (closestArcDist > Math.PI) {closestArcDist = closestArcDist - Math.PI}

    // Some odd math to avoid doing the "long way 'round
    if ((myFacing > velocityFacing && myFacing - velocityFacing < (Math.PI)) ||
         myFacing < velocityFacing && velocityFacing - myFacing > (Math.PI)) {
      _myBody.SetAngularVelocity(_myBody.GetAngularVelocity() - (Math.min(0.2, closestArcDist/ 4.)));
    } else {
      _myBody.SetAngularVelocity(_myBody.GetAngularVelocity() + (Math.min(0.2, closestArcDist / 4.)));

    }

  }

  // Push yourself in a random direction
  // TODO: Consider adjusting this based on the person's volume in the hangout
  private function generateRandomPush():void {
    var randomVec:b2Vec2 = new b2Vec2(ToddUtils.randomNum(-15.0, 15.0), ToddUtils.randomNum(-15.0, 15.0));
    _myBody.ApplyImpulse(randomVec, _myBody.GetWorldCenter());
    _myBody.ApplyTorque(ToddUtils.randomNum(-2.0,2.0));
    TweenLite.delayedCall(ToddUtils.randomNum(3.0,5.0), generateRandomPush);
  }

  // Create the actual object in Box2D World
  private function createPhysObj():void {

    // Just to make our code a little simpler
    const ratio:Number = HangoutInSpace.RATIO;

    var centerBit:b2CircleShape = new b2CircleShape();
    centerBit.SetRadius(30 / ratio);

    var fixtureDef:b2FixtureDef = new b2FixtureDef();
    fixtureDef.density = 1.0;
    fixtureDef.friction = 0.0;
    fixtureDef.restitution = 0.6;
    fixtureDef.shape = centerBit;

    var bodyDef:b2BodyDef = new b2BodyDef();
    bodyDef.position = new b2Vec2(ToddUtils.randomInt(30, HangoutInSpace.HANGOUT_WIDTH-30) / ratio,
        ToddUtils.randomInt(10, HangoutInSpace.HANGOUT_HEIGHT-30) / ratio);
    bodyDef.type = b2Body.b2_dynamicBody;
    _myBody = _world.CreateBody(bodyDef);
    _myBody.SetAngularDamping(1.0);
    _myBody.CreateFixture(fixtureDef);

    var bottomBar:b2PolygonShape = new b2PolygonShape();



    bottomBar.SetAsArray([new b2Vec2(31 / ratio, 3 / ratio),
      new b2Vec2(46 / ratio, 20 / ratio),
      new b2Vec2(37.5 / ratio, 28 / ratio),
      new b2Vec2(24 / ratio, 36 / ratio),
      new b2Vec2(0 / ratio, 40 / ratio),
      new b2Vec2(-23.5 / ratio, 36 / ratio),
      new b2Vec2(-44.5 / ratio, 19 / ratio),
      new b2Vec2(-30 / ratio, 2 / ratio)], 8);

    // Do I need to close this off?
    fixtureDef.shape = bottomBar;
    _myBody.CreateFixture(fixtureDef);
    _myBody.SetActive(true);

  }

  public function get hangoutUser():HangoutParticipant {
    return _hangoutUser;
  }

  public function goAway():void {
    _world.DestroyBody(_myBody);
    TweenLite.killDelayedCallsTo(generateRandomPush);
    TweenLite.to(_costume, 1.0, {alpha:0, onComplete:killCostume})
  }

  private function killCostume():void {
     _costume.parent.removeChild(_costume);
  }

  // Apply a fake explosion to yourself based on a mouse click in the world
  public function mouseClickAt(mouseClick:b2Vec2):void {
    var vectorToMe:b2Vec2 = _myBody.GetWorldCenter().Copy();
    vectorToMe.Subtract(mouseClick);
    var distToMouse:Number = vectorToMe.Length();
    var power:Number = 10 * (400/HangoutInSpace.RATIO - distToMouse);
    if (power < 0) power = 0;
    vectorToMe.Normalize();
    vectorToMe.Multiply(power);
    
    _myBody.ApplyImpulse(vectorToMe, _myBody.GetWorldCenter());

  }
}
}
